#include <linux/myhead_for_driver.h>

#define DEV_NAME "si7006_driver"
#define CLS_NAME "si7006_driver_info"

struct i2c_client *client_global;
u8 reg_from_usr;

int major;
int minor;
struct cdev *cdev;
struct class *cls;
struct device *dev;
int can_send = 0;

struct timer_list k_tim;

void k_tim_event(struct timer_list *timer) // 内核定时器
{
    mod_timer(&k_tim, jiffies + HZ * 3);
    can_send = 1;
    return;
}

u16 read_tem_hum(u8 reg)
{
    u8 addr_buf = reg;
    u8 data_buf[2];
    u16 res;
    struct i2c_msg msgs[] = {
        {.addr = client_global->addr,
         .flags = 0,
         .len = 1,
         .buf = &addr_buf},
        {.addr = client_global->addr,
         .flags = 1,
         .len = 2,
         .buf = data_buf},
    };
    res = i2c_transfer(client_global->adapter, msgs, 2);
    if (ARRAY_SIZE(msgs) != res)
    {
        printk("read_tem error,pls try again!\n");
        return -EAGAIN;
    }
    else
    {
        res = data_buf[0];
        res = res << 8;
        res |= data_buf[1];
        return res;
    }
}
int si7006_open(struct inode *inode, struct file *file)
{
    return 0;
}

ssize_t si7006_read(struct file *file, char __user *ubuf, size_t size, loff_t *loff)
{
    u16 data;
    data = read_tem_hum(reg_from_usr);
    if (data < 0)
    {
        printk("read_tem error,pls try again!\n");
        return -EAGAIN;
    }
    else
    {
        copy_to_user(ubuf, &data, size);
    }
    return 0;
}

ssize_t si7006_write(struct file *file, const char __user *ubuf, size_t size, loff_t *loff)
{
    copy_from_user(&reg_from_usr, ubuf, size); // 错误判断

    return 0;
}

int si7006_close(struct inode *inode, struct file *file)
{
    return 0;
}

long si7006_ioctl(struct file *file, unsigned int cmd, unsigned long args)
{
    int send_ = 1;
    if (can_send)
    {
        copy_to_user((void *)(args), &send_, ((cmd >> 16) & 0x3FFF));
        can_send = 0;
    }

    return 0;
}

const static struct file_operations fops = {
    .open = si7006_open,
    .read = si7006_read,
    .write = si7006_write,
    .release = si7006_close,
    .unlocked_ioctl = si7006_ioctl,
};

int dev_create_auto(void)
{
    int err, res;
    dev_t dev_num;
    cdev = cdev_alloc();
    if (NULL == cdev)
    {
        err = -EPERM;
        goto ERR1;
    }
    cdev_init(cdev, &fops);
    if ((res = alloc_chrdev_region(&dev_num, 0, 1, DEV_NAME)))
    {
        err = res;
        goto ERR2;
    }
    major = MAJOR(dev_num);
    minor = MINOR(dev_num);
    if ((res = cdev_add(cdev, MKDEV(major, minor), 1)))
    {
        err = res;
        goto ERR3;
    }
    cls = class_create(THIS_MODULE, CLS_NAME);
    if (IS_ERR(cls))
    {
        err = PTR_ERR(cls);
        goto ERR4;
    }
    dev = device_create(cls, NULL, MKDEV(major, minor), NULL, DEV_NAME);
    if (IS_ERR(dev))
    {
        err = PTR_ERR(dev);
        goto ERR5;
    }

    return 0;

ERR5:
    class_destroy(cls);
ERR4:
    cdev_del(cdev);
ERR3:
    unregister_chrdev_region(MKDEV(major, minor), 1);
ERR2:
    kfree(cdev);
ERR1:
    return err;
}

int si7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    u16 data;
    int res;
    client_global = client;

    if ((res = dev_create_auto()))
    {
        printk("dev_create_auto error");
        return res;
    }

    data = read_tem_hum(0xE5);
    printk("hum is %d\n", data);

    k_tim.expires = jiffies + 1;
    timer_setup(&k_tim, k_tim_event, 0);
    add_timer(&k_tim);
    return 0;
}

int si7006_remove(struct i2c_client *client)
{
    del_timer(&k_tim);
    // if(can_remove_...)
    device_destroy(cls, MKDEV(major, minor));
    class_destroy(cls);
    cdev_del(cdev);
    unregister_chrdev_region(MKDEV(major, minor), 1);
    kfree(cdev);
    return 0;
}

const struct of_device_id of_table[] = {
    {.compatible = "rhf,si7006"},
    {},
};

struct i2c_driver si7006_drv = {
    .probe = si7006_probe,
    .remove = si7006_remove,
    .driver = {
        .name = "rhf",
        .of_match_table = of_table,
    },
};

module_i2c_driver(si7006_drv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("rhf xxx@xxx.com");